#define _GNU_SOURCE // For NETLINK_ROUTE, RTMGRP_LINK, etc.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>      // <-- 添加: 包含 errno 和 EINTR
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h> // For if_nametoindex

#define BUFSIZE 8192

// --- 手动实现 NLMSG_NEXT 的逻辑 ---
// 计算对齐
#ifndef NLMSG_ALIGN
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
#endif

// 获取下一个消息的指针并更新剩余长度
static inline struct nlmsghdr* my_nlmsg_next(struct nlmsghdr *nlh, ssize_t *remaining_len) {
    int aligned_len = NLMSG_ALIGN(nlh->nlmsg_len);
    *remaining_len -= aligned_len;
    return (struct nlmsghdr*)((char*)nlh + aligned_len);
}
// --- 手动实现结束 ---


// Global variables to store timestamps and state
static struct timespec time1 = {0, 0}; // Time when primary goes down
static struct timespec time2 = {0, 0}; // Time when secondary goes up
static int primary_index = -1;         // Interface index of primary
static int secondary_index = -1;       // Interface index of secondary
static int primary_seen_down = 0;      // Flag if primary down event was seen
static int test_completed = 0;         // Flag to stop the loop after measurement

// Function to print usage
void print_usage(const char *prog_name) {
    fprintf(stderr, "用法: sudo %s <主接口名> <备用接口名>\n", prog_name);
    fprintf(stderr, "说明: 程序启动后，手动物理断开主接口或执行 'sudo ip link set <主接口名> down'。\n");
    fprintf(stderr, "      然后，手动物理连接备用接口或执行 'sudo ip link set <备用接口名> up'。\n");
    fprintf(stderr, "      程序将检测状态变化并计算切换延迟。\n");
}

// Function to get interface index by name
int get_interface_index(const char *iface_name) {
    int index = if_nametoindex(iface_name);
    if (index == 0) {
        perror("无法获取接口索引 (if_nametoindex)");
        fprintf(stderr, "请检查接口名称 '%s' 是否正确。\n", iface_name);
    }
    return index;
}

// Function to process incoming netlink messages
// 参数 nh 指向 recv 接收到的缓冲区的开始, len 是接收的总字节数
void handle_netlink_message(struct nlmsghdr *nh, ssize_t len, const char *primary_name, const char *secondary_name) {
    // 使用临时指针 current_nh 来遍历缓冲区中的所有消息
    struct nlmsghdr *current_nh;
    ssize_t remaining_len; // 跟踪缓冲区中剩余的字节数

    // 初始化指针和剩余长度
    current_nh = nh;
    remaining_len = len;

    // 遍历所有接收到的 netlink 消息
    // 循环条件：检查 current_nh 是否有效且在接收缓冲区内
    while (NLMSG_OK(current_nh, (unsigned int)remaining_len)) {
        
        if (current_nh->nlmsg_type == NLMSG_DONE) {
            break;
        }
        if (current_nh->nlmsg_type == NLMSG_ERROR) {
            struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(current_nh);
            fprintf(stderr, "Netlink error: %d\n", err->error);
            // 移动到下一个消息
            current_nh = my_nlmsg_next(current_nh, &remaining_len);
            continue; // 处理下一个消息或退出循环
        }
        if (current_nh->nlmsg_type != RTM_NEWLINK && current_nh->nlmsg_type != RTM_DELLINK) {
            // 移动到下一个消息
            // 修复: 使用我们手动实现的函数
            current_nh = my_nlmsg_next(current_nh, &remaining_len);
            continue; // Not a link event we are interested in
        }

        struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(current_nh);
        int ifindex = ifi->ifi_index;

        // Check if the event is for our monitored interfaces
        if (ifindex != primary_index && ifindex != secondary_index) {
             // 移动到下一个消息
             current_nh = my_nlmsg_next(current_nh, &remaining_len);
            continue;
        }

        // Get interface name from attributes
        char ifname[IFNAMSIZ] = {0};
        struct rtattr *rta = IFLA_RTA(ifi);
        int rtalen = IFLA_PAYLOAD(current_nh); // 使用 current_nh

        for (; RTA_OK(rta, rtalen); rta = RTA_NEXT(rta, rtalen)) {
            if (rta->rta_type == IFLA_IFNAME) {
                strncpy(ifname, (char *)RTA_DATA(rta), sizeof(ifname) - 1);
                break;
            }
        }

        // Determine event type and state
        // 接口真正 up 通常意味着 administratively UP (IFF_UP) 且 operationally UP (IFF_RUNNING)
        int is_up = (ifi->ifi_flags & IFF_UP) && (ifi->ifi_flags & IFF_RUNNING);
        // Down 事件可以是 RTM_DELLINK 或者 IFF_UP 标志被清除
        int is_down_event = (current_nh->nlmsg_type == RTM_DELLINK) || !is_up;

        // --- Logic for Primary Interface (lan1) ---
        if (ifindex == primary_index) {
            if (is_down_event && !primary_seen_down) {
                if (clock_gettime(CLOCK_MONOTONIC, &time1) == -1) {
                    perror("获取 time1 时间戳失败");
                    // 移动到下一个消息再退出，保持逻辑清晰
                    current_nh = my_nlmsg_next(current_nh, &remaining_len);
                    exit(EXIT_FAILURE);
                }
                printf("[%ld.%09ld] 检测到主接口 %s (索引 %d) DOWN 事件 (类型: %s)。\n",
                       time1.tv_sec, time1.tv_nsec, primary_name, primary_index,
                       current_nh->nlmsg_type == RTM_DELLINK ? "RTM_DELLINK" : "IFF_UP cleared");
                primary_seen_down = 1;
            }
        }

        // --- Logic for Secondary Interface (lan2) ---
        // 只有在主接口已经 down 过的情况下，才关心备用接口的 up 事件
        if (ifindex == secondary_index && primary_seen_down && !test_completed) {
            if (!is_down_event) { // Interface is coming UP (IFF_UP && IFF_RUNNING)
                if (clock_gettime(CLOCK_MONOTONIC, &time2) == -1) {
                    perror("获取 time2 时间戳失败");
                    // 移动到下一个消息再退出
                    current_nh = my_nlmsg_next(current_nh, &remaining_len);
                    exit(EXIT_FAILURE);
                }
                printf("[%ld.%09ld] 检测到备用接口 %s (索引 %d) UP 事件 (IFF_UP && IFF_RUNNING)。\n",
                       time2.tv_sec, time2.tv_nsec, secondary_name, secondary_index);

                // --- Calculate and report delay ---
                if (time1.tv_sec != 0) { // Ensure time1 was set
                    long long delay_ns = (time2.tv_sec - time1.tv_sec) * 1000000000LL + (time2.tv_nsec - time1.tv_nsec);
                    printf("\n--- 测试结果 ---\n");
                    printf("主接口 %s Down 时间: %ld.%09ld 秒\n", primary_name, time1.tv_sec, time1.tv_nsec);
                    printf("备用接口 %s Up 时间:   %ld.%09ld 秒\n", secondary_name, time2.tv_sec, time2.tv_nsec);
                    printf("网络接口切换延迟: %lld 纳秒 (%.6f 毫秒)\n", delay_ns, delay_ns / 1000000.0);
                } else {
                    printf("警告: 未记录到主接口 Down 的时间，无法计算延迟。\n");
                }
                test_completed = 1; // Exit the main loop
                // 在计算完延迟后，可以考虑退出循环或继续监听（如果需要多次测试）
                // 这里我们选择退出循环，因为任务已完成
                // 不需要再移动 current_nh，因为循环会因 test_completed 而退出
                break; 
            }
        }
        
        // *** 关键修复 ***
        // 在循环的末尾，正确地移动到下一个 netlink 消息
        // 使用我们手动实现的函数
        current_nh = my_nlmsg_next(current_nh, &remaining_len); 
    }
    // 循环结束
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        print_usage(argv[0]);
        exit(EXIT_FAILURE);
    }

    const char *primary_name = argv[1];
    const char *secondary_name = argv[2];

    printf("主接口: %s\n", primary_name);
    printf("备用接口: %s\n", secondary_name);

    // Get interface indices
    primary_index = get_interface_index(primary_name);
    if (primary_index <= 0) exit(EXIT_FAILURE);

    secondary_index = get_interface_index(secondary_name);
    if (secondary_index <= 0) exit(EXIT_FAILURE);

    // Create netlink socket
    int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sock < 0) {
        perror("创建 netlink socket 失败");
        exit(EXIT_FAILURE);
    }

    // Bind socket to listen for link events
    struct sockaddr_nl addr;
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    // RTMGRP_LINK 监听 IFF_UP 标志变化和 RTM_NEWLINK/RTM_DELLINK
    addr.nl_groups = RTMGRP_LINK; 

    if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("绑定 netlink socket 失败");
        close(sock);
        exit(EXIT_FAILURE);
    }

    printf("正在监听接口状态变化... 请执行物理插拔或使用 'ip link set' 命令。\n");
    printf("等待主接口 %s 变为 DOWN...\n", primary_name);

    // Main loop to receive messages
    char buf[BUFSIZE];
    while (!test_completed) {
        ssize_t len = recv(sock, buf, sizeof(buf), 0);
        if (len < 0) {
            // 修复: 包含 errno.h 后，errno 和 EINTR 才能被识别
            if (errno == EINTR) {
                continue; // Interrupted by signal, try again
            }
            perror("接收 netlink 消息失败");
            break;
        }
        if (len == 0) {
            fprintf(stderr, "Netlink socket closed unexpectedly.\n");
            break;
        }

        // 修复: 传递接收到的缓冲区指针和实际长度给处理函数
        handle_netlink_message((struct nlmsghdr *)buf, len, primary_name, secondary_name);
    }

    printf("测试完成，关闭 socket。\n");
    close(sock);
    return 0;
}



